// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package modload

import (
	"cmd/go/internal/base"
	"cmd/go/internal/cfg"
	"cmd/go/internal/mvs"
	"cmd/go/internal/par"
	"context"
	"fmt"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"sync/atomic"

	"golang.org/x/mod/module"
	"golang.org/x/mod/semver"
)

// capVersionSlice返回s，其cap减小到其长度。
func capVersionSlice(s []module.Version) []module.Version {
	return s[:len(s):len(s)]
}

// 需求表示逻辑上不可变的根模块需求集。
type Requirements struct {
	// 深度是计算需求图的深度。
	// None
	// 如果渴望，则该图包括所有可传递的需求，而不考虑深度。
	// None
	// 如果是惰性的，则图形仅包括根模块、显式
	// 这些根模块的需求，以及
	// *非惰性*根模块。
	depth modDepth

	// rootModules是主程序显式要求的模块版本集
	// 模块，按长度排序并加盖。它可能包含重复项，并且可能
	// 包含给定模块路径的多个版本。
	rootModules    []module.Version
	maxRootVersion map[string]string

	// direct是我们认为模块提供的一组模块路径
	// 由主模块中的包或测试直接导入的包。
	// None
	// “直接”映射控制用“直接”标记的模块
	// 注释，并可能影响列出的模块
	// 显式根（与仅间接依赖项相比）。然而，它不应该这样做
	// 对整个构建列表具有语义影响。
	// None
	// 初始直接映射是从现有的
	// go.mod文件中的注释（或缺少注释）。它由
	// 包加载器：如果是新的依赖项，则可以升级为直接依赖项
	// 观察到直接进口，并且在试验期间可能降级为间接进口
	// “go mod tidy”或“go mod vendor”。
	// None
	// 直接映射由模块路径而不是模块版本设置关键帧。当
	// 模块的选定版本发生更改，我们假设如果
	// 以前的版本是直接依赖的。这种假设可能不成立
	// 罕见的情况（如依赖项拆分出嵌套模块或合并
	// 将模块嵌套回父模块中）。
	direct map[string]bool

	graphOnce sync.Once    // 守卫写入（但不从）图形
	graph     atomic.Value // 缓存图
}

// cachedGraph是非nil*ModuleGraph，以及发现的任何错误
// 在加载图表时。
type cachedGraph struct {
	mg  *ModuleGraph
	err error // 如果err为非零，则mg可能不完整（但必须仍然为非零）。
}

// 需求是主模块的需求图。
// None
// 如果主模块的go.mod文件已加载，则始终为非nil。
// None
// 此变量应仅从loadModFile函数读取，并且应
// 只能在loadModFile和CommitRequests函数中写入。
// 需要或产生*需求的所有其他功能应
// 接受和/或返回显式参数。
var requirements *Requirements

// newRequirements返回具有给定根模块的新需求集。
// 根的依赖项将在第一次调用
// 图表法。
// None
// rootModules切片必须根据module.Sort进行排序。
// 调用方在传递后不得修改rootModules切片或直接映射
// 它们符合新的要求。
// None
// 如果vendoring有效，则调用方必须在返回的
// *要求优先于任何其他方法。
func newRequirements(depth modDepth, rootModules []module.Version, direct map[string]bool) *Requirements {
	for i, m := range rootModules {
		if m == Target {
			panic(fmt.Sprintf("newRequirements called with untrimmed build list: rootModules[%v] is Target", i))
		}
		if m.Path == "" || m.Version == "" {
			panic(fmt.Sprintf("bad requirement: rootModules[%v] = %v", i, m))
		}
		if i > 0 {
			prev := rootModules[i-1]
			if prev.Path > m.Path || (prev.Path == m.Path && semver.Compare(prev.Version, m.Version) > 0) {
				panic(fmt.Sprintf("newRequirements called with unsorted roots: %v", rootModules))
			}
		}
	}

	rs := &Requirements{
		depth:          depth,
		rootModules:    capVersionSlice(rootModules),
		maxRootVersion: make(map[string]string, len(rootModules)),
		direct:         direct,
	}

	for _, m := range rootModules {
		if v, ok := rs.maxRootVersion[m.Path]; ok && cmpVersion(v, m.Version) >= 0 {
			continue
		}
		rs.maxRootVersion[m.Path] = m.Version
	}
	return rs
}

// initVendor从给定的供应商模块列表初始化rs.graph
// 依赖项，覆盖通常从模块加载的图形
// 要求。
func (rs *Requirements) initVendor(vendorList []module.Version) {
	rs.graphOnce.Do(func() {
		mg := &ModuleGraph{
			g: mvs.NewGraph(cmpVersion, []module.Version{Target}),
		}

		if rs.depth == lazy {
			// 惰性模块的根应该已经包括
			// 供应商列表，因为供应商模块与其他模块相同
			// 通过延迟加载“导入不变量”维护为根。
			// None
			// 为了确定，我们会在这里再次检查。
			inconsistent := false
			for _, m := range vendorList {
				if v, ok := rs.rootSelected(m.Path); !ok || v != m.Version {
					base.Errorf("go: vendored module %v should be required explicitly in go.mod", m)
					inconsistent = true
				}
			}
			if inconsistent {
				base.Fatalf("go: %v", errGoModDirty)
			}

			// 现在我们可以有效地“修剪”模块图的其余部分
			// out”，类似于更激进的延迟加载版本：在供应商模式下，
			// 根需求*是*完整的模块图。
			mg.g.Require(Target, rs.rootModules)
		} else {
			// 主模块的可传递性需求通常不可用
			// 从供应商目录中，我们不知道我们是如何从
			// 最终生成列表的根。
			// None
			// 相反，我们将注入一个伪“vendor/modules.txt”模块，该模块提供
			// 这些可传递的依赖项，并将其标记为main的依赖项
			// 单元这使我们能够省略模块的实际结构
			// 图形，但仍然区分直接和间接
			// 依赖关系。
			vendorMod := module.Version{Path: "vendor/modules.txt", Version: ""}
			mg.g.Require(Target, append(rs.rootModules, vendorMod))
			mg.g.Require(vendorMod, vendorList)
		}

		rs.graph.Store(cachedGraph{mg, nil})
	})
}

// rootSelected返回具有给定模块的根依赖项的版本
// 如果模块不是根目录，则为path或zero module.Version和ok=false
// 附属国。
func (rs *Requirements) rootSelected(path string) (version string, ok bool) {
	if path == Target.Path {
		return Target.Version, true
	}
	if v, ok := rs.maxRootVersion[path]; ok {
		return v, true
	}
	return "", false
}

// 如果根列表包含多个需求，则hasRedundantRoot返回true
// 相同模块或主模块任何版本的要求。
// 应该删减冗余需求，但它们可能会影响版本
// 选择
func (rs *Requirements) hasRedundantRoot() bool {
	for i, m := range rs.rootModules {
		if m.Path == Target.Path || (i > 0 && m.Path == rs.rootModules[i-1].Path) {
			return true
		}
	}
	return false
}

// Graph返回从当前模块加载的模块需求图
// 根模块（由RootModules报告）。
// None
// 图形总是尽最大努力加载需求图，尽管存在任何问题
// 错误，并始终返回非nil*ModuleGraph。
// None
// 如果任何相关模块的要求未能加载，则还应绘制图表
// 返回*mvs.BuildListError类型的非零错误。
func (rs *Requirements) Graph(ctx context.Context) (*ModuleGraph, error) {
	rs.graphOnce.Do(func() {
		mg, mgErr := readModGraph(ctx, rs.depth, rs.rootModules)
		rs.graph.Store(cachedGraph{mg, mgErr})
	})
	cached := rs.graph.Load().(cachedGraph)
	return cached.mg, cached.err
}

// IsDirect返回给定模块是否直接提供包
// 由主模块中的包或测试导入。
func (rs *Requirements) IsDirect(path string) bool {
	return rs.direct[path]
}

// ModuleGraph表示模块依赖关系的完整图
// 一个主模块的。
// None
// 如果主模块是延迟加载的，则图形不包括
// 非根（隐式）依赖的可传递依赖。
type ModuleGraph struct {
	g         *mvs.Graph
	loadCache par.Cache // 模块版本→ 总和误差

	buildListOnce sync.Once
	buildList     []module.Version
}

// summaryError是非nil modFileSummary或非nil错误
// 在读取或分析该摘要时遇到。
type summaryError struct {
	summary *modFileSummary
	err     error
}

var readModGraphDebugOnce sync.Once

// readModGraph读取并返回从
// 给定根。
// None
// 与LoadModGraph不同，readModGraph不尝试诊断或更新
// 不一致的根。
func readModGraph(ctx context.Context, depth modDepth, roots []module.Version) (*ModuleGraph, error) {
	if depth == lazy {
		readModGraphDebugOnce.Do(func() {
			for _, f := range strings.Split(os.Getenv("GODEBUG"), ",") {
				switch f {
				case "lazymod=log":
					debug.PrintStack()
					fmt.Fprintf(os.Stderr, "go: read full module graph.\n")
				case "lazymod=strict":
					debug.PrintStack()
					base.Fatalf("go: read full module graph (forbidden by GODEBUG=lazymod=strict).")
				}
			}
		})
	}

	var (
		mu       sync.Mutex // 装载过程中的防护装置mg.g和HASSERROR
		hasError bool
		mg       = &ModuleGraph{
			g: mvs.NewGraph(cmpVersion, []module.Version{Target}),
		}
	)
	mg.g.Require(Target, roots)

	var (
		loadQueue    = par.NewQueue(runtime.GOMAXPROCS(0))
		loadingEager sync.Map // 模块版本→ 无已通过或正在通过根加载的模块集
	)

	// loadOne同步加载模块m的明确需求。
	// 它不会加载m的可传递需求，即使go版本在
	// m的go.mod文件表示急于加载。
	loadOne := func(m module.Version) (*modFileSummary, error) {
		cached := mg.loadCache.Do(m, func() interface{} {
			summary, err := goModSummary(m)

			mu.Lock()
			if err == nil {
				mg.g.Require(m, summary.require)
			} else {
				hasError = true
			}
			mu.Unlock()

			return summaryError{summary, err}
		}).(summaryError)

		return cached.summary, cached.err
	}

	var enqueue func(m module.Version, depth modDepth)
	enqueue = func(m module.Version, depth modDepth) {
		if m.Version == "none" {
			return
		}

		if depth == eager {
			if _, dup := loadingEager.LoadOrStore(m, nil); dup {
				// m已排队等待加载。由于迫不及待的装载可能
				// 遵循需求图中的周期，我们需要提前返回
				// 避免使加载队列无限长。
				return
			}
		}

		loadQueue.Add(func() {
			summary, err := loadOne(m)
			if err != nil {
				return // findError稍后将报告错误。
			}

			// 如果m的go.mod文件中的版本意味着急于加载，那么我们就不能
			// 假设m的明确要求（由loadOne添加）为
			// 足以构建它包含的包。我们必须把它装满
			// 传递依赖关系图，以确保我们看到所有相关的
			// 依赖关系。
			if depth == eager || summary.depth == eager {
				for _, r := range summary.require {
					enqueue(r, eager)
				}
			}
		})
	}

	for _, m := range roots {
		enqueue(m, depth)
	}
	<-loadQueue.Idle()

	if hasError {
		return mg, mg.findError()
	}
	return mg, nil
}

// RequiredBy返回图中模块m所需的依赖项，
// 或者，如果模块m的依赖项不相关（例如
// 通过延迟加载删除）。
// None
// 调用者不能修改返回的切片，但可以安全地附加到它
// 并且可能依赖于它不被修改。
func (mg *ModuleGraph) RequiredBy(m module.Version) (reqs []module.Version, ok bool) {
	return mg.g.RequiredBy(m)
}

// Selected返回具有给定路径的模块的选定版本。
// None
// 如果未选择任何版本，则所选版本返回“无”。
func (mg *ModuleGraph) Selected(path string) (version string) {
	return mg.g.Selected(path)
}

// WalkBroadthFirst为每个模块按宽度优先顺序调用f一次
// 图形中显示的“无”以外的版本，无论
// 已选择该版本。
func (mg *ModuleGraph) WalkBreadthFirst(f func(m module.Version)) {
	mg.g.WalkBreadthFirst(f)
}

// BuildList返回图形中所有模块的选定版本，
// 从目标开始。
// None
// 列表中其余元素的顺序是确定的
// 但是武断。
// None
// 调用方不能修改返回的列表，但可以安全地附加到列表中
// 并且可能依赖于它不被修改。
func (mg *ModuleGraph) BuildList() []module.Version {
	mg.buildListOnce.Do(func() {
		mg.buildList = capVersionSlice(mg.g.BuildList())
	})
	return mg.buildList
}

func (mg *ModuleGraph) findError() error {
	errStack := mg.g.FindPath(func(m module.Version) bool {
		cached := mg.loadCache.Get(m)
		return cached != nil && cached.(summaryError).err != nil
	})
	if len(errStack) > 0 {
		err := mg.loadCache.Get(errStack[len(errStack)-1]).(summaryError).err
		var noUpgrade func(from, to module.Version) bool
		return mvs.NewBuildListError(err, errStack, noUpgrade)
	}

	return nil
}

func (mg *ModuleGraph) allRootsSelected() bool {
	roots, _ := mg.g.RequiredBy(Target)
	for _, m := range roots {
		if mg.Selected(m.Path) != m.Version {
			return false
		}
	}
	return true
}

// LoadModGraph加载并返回主模块的模块依赖关系图，
// 没有加载任何包。
// None
// 如果goVersion字符串为非空，则返回的图形为图形
// 按照给定的Go版本（而不是指定的版本）进行解释
// 在go.mod文件中）。
// None
// 模块在LoadPackages中自动（并延迟）加载：
// 仅当LoadPackages不可用时才需要调用LoadModGraph，
// 通常在关心模块但不关心特定包的命令中。
func LoadModGraph(ctx context.Context, goVersion string) *ModuleGraph {
	rs := LoadModFile(ctx)

	if goVersion != "" {
		depth := modDepthFromGoVersion(goVersion)
		if depth == eager && rs.depth != eager {
			// 使用newRequirements而不是convertDepth，因为convertDepth
			// 也更新根；这里，我们要报告未修改的根
			// 即使它们看起来不一致。
			rs = newRequirements(eager, rs.rootModules, rs.direct)
		}

		mg, err := rs.Graph(ctx)
		if err != nil {
			base.Fatalf("go: %v", err)
		}
		return mg
	}

	rs, mg, err := expandGraph(ctx, rs)
	if err != nil {
		base.Fatalf("go: %v", err)
	}

	commitRequirements(ctx, modFileGoVersion(), rs)
	return mg
}

// expandGraph从rs加载完整的模块图。
// None
// 如果完整的图显示rs的某个根实际上不是
// expandGraph在其路径的选定版本中计算一组新的根
// 它们是一致的。（当实现延迟加载时，这可能会导致
// 由于以前被删减的需求而升级到其他模块
// 出去。）
// None
// expandGraph返回更新后的根以及加载的模块图
// 从这些根和加载该图形时遇到的任何错误。
// expandGraph返回非nil需求和非nil图，而不考虑
// 错误。出现错误时，可能无法更新根以保持一致。
func expandGraph(ctx context.Context, rs *Requirements) (*Requirements, *ModuleGraph, error) {
	mg, mgErr := rs.Graph(ctx)
	if mgErr != nil {
		// 没有图形，我们无法更新根：我们不知道是哪个根
		// 将选择可传递依赖项的版本。
		return rs, mg, mgErr
	}

	if !mg.allRootsSelected() {
		// rs的根与图的其余部分不一致。使现代化
		// 他们在一个急切的模块中，对于整个构建列表来说，这是一个不可操作的选项-
		// 它只是促进了以前的可传递需求
		// 根-但在惰性模块中，它可能会拉入以前不相关的内容
		// 可传递依赖项。

		newRS, rsErr := updateRoots(ctx, rs.direct, rs, nil, nil, false)
		if rsErr != nil {
			// 无法更新根，可能是因为传递函数中的错误
			// 更新需要依赖项。返回原始要求
			// 相反
			return rs, mg, rsErr
		}
		rs = newRS
		mg, mgErr = rs.Graph(ctx)
	}

	return rs, mg, mgErr
}

// None
// 添加到现有生成列表，然后调整版本（以及添加或删除
// 根据需要）直到mustSelect中的每个模块在
// 给定版本。
// None
// （请注意，新添加的模块可能不会在结果中选择。）
// 构建列表：它们可能低于现有需求或与
// mustSelect中的版本。）
// None
// 如果mustSelect中列出的版本互不兼容（由于以下原因之一）：
// 列出的模块需要另一个版本的更高版本），EditBuildList
// 返回*值，并使生成列表保持其以前的状态。
// None
// 成功后，EditBuildList会报告所选版本的任何模块
// 在构建列表中，可能已作为
// 后果
func EditBuildList(ctx context.Context, add, mustSelect []module.Version) (changed bool, err error) {
	rs, changed, err := editRequirements(ctx, LoadModFile(ctx), add, mustSelect)
	if err != nil {
		return false, err
	}
	commitRequirements(ctx, modFileGoVersion(), rs)
	return changed, err
}

// ConstraintError描述EditBuildList中不一致的约束
type ConstraintError struct {
	// 冲突列出mustSelect中每个版本的冲突源
	// 由于中某些其他版本的要求，无法选择
	// 必须选择。
	Conflicts []Conflict
}

func (e *ConstraintError) Error() string {
	b := new(strings.Builder)
	b.WriteString("version constraints conflict:")
	for _, c := range e.Conflicts {
		fmt.Fprintf(b, "\n\t%v requires %v, but %v is requested", c.Source, c.Dep, c.Constraint)
	}
	return b.String()
}

// 冲突文档源需要Dep，这与约束冲突。
// （也就是说，Dep的模块路径与Constraint相同，但版本更高。）
type Conflict struct {
	Source     module.Version
	Dep        module.Version
	Constraint module.Version
}

// tidyRoots将根依赖项修剪到需要的最低要求
// 两者都保留pkgs中所有包的相同版本，并满足
// 延迟加载不变量（如果适用）。
func tidyRoots(ctx context.Context, rs *Requirements, pkgs []*loadPkg) (*Requirements, error) {
	if rs.depth == eager {
		return tidyEagerRoots(ctx, rs.direct, pkgs)
	}
	return tidyLazyRoots(ctx, rs.direct, pkgs)
}

func updateRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
	if rs.depth == eager {
		return updateEagerRoots(ctx, direct, rs, add)
	}
	return updateLazyRoots(ctx, direct, rs, pkgs, add, rootsImported)
}

// TidyLazRoots返回维护
// 给定包的go.mod文件的“延迟加载”不变量：
// None
// 1.对于每个标记为pkgInAll的包，提供
// 包作为根目录包含。
// 2.对于所有包，提供该包的模块要么保留
// 在同一版本中选择，或由
// 根
// None
// 如果提供软件包的任何模块已升级到其先前版本之上，
// 版本时，调用方可能需要重新加载并重新计算包图。
// None
// 为了确保加载过程最终收敛，调用方应该
// 从整洁的根集中添加任何所需的根（不删除现有的不整洁根）
// 根）直到根集合收敛。
func tidyLazyRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
	var (
		roots        []module.Version
		pathIncluded = map[string]bool{Target.Path: true}
	)
	// 我们首先在“all”中为每个包添加根。
	// None
	// 一旦这样做了，我们可能仍然需要添加更多的根来覆盖升级或升级
	// 否则，“all”中缺少包的测试依赖项。对于那些测试
	// 依赖项，我们更喜欢为导入时间较短的包添加根
	// 首先，基于这样一种理论，即这些模块的需求将
	// 倾向于填写其可传递进口的要求（具有
	// 更深的导入堆栈）。因此，我们在一个深度处添加缺少的依赖项
	// 有一段时间，从“全部”中的包开始，向外扩展
	// 直到我们扫描了所有装载的包裹。
	var (
		queue  []*loadPkg
		queued = map[*loadPkg]bool{}
	)
	for _, pkg := range pkgs {
		if !pkg.flags.has(pkgInAll) {
			continue
		}
		if pkg.fromExternalModule() && !pathIncluded[pkg.mod.Path] {
			roots = append(roots, pkg.mod)
			pathIncluded[pkg.mod.Path] = true
		}
		queue = append(queue, pkg)
		queued[pkg] = true
	}
	module.Sort(roots)
	tidy := newRequirements(lazy, roots, direct)

	for len(queue) > 0 {
		roots = tidy.rootModules
		mg, err := tidy.Graph(ctx)
		if err != nil {
			return nil, err
		}

		prevQueue := queue
		queue = nil
		for _, pkg := range prevQueue {
			m := pkg.mod
			if m.Path == "" {
				continue
			}
			for _, dep := range pkg.imports {
				if !queued[dep] {
					queue = append(queue, dep)
					queued[dep] = true
				}
			}
			if pkg.test != nil && !queued[pkg.test] {
				queue = append(queue, pkg.test)
				queued[pkg.test] = true
			}
			if !pathIncluded[m.Path] {
				if s := mg.Selected(m.Path); cmpVersion(s, m.Version) < 0 {
					roots = append(roots, m)
				}
				pathIncluded[m.Path] = true
			}
		}

		if len(roots) > len(tidy.rootModules) {
			module.Sort(roots)
			tidy = newRequirements(lazy, roots, tidy.direct)
		}
	}

	_, err := tidy.Graph(ctx)
	if err != nil {
		return nil, err
	}
	return tidy, nil
}

// updateLazyRoots返回一组根需求，这些根需求维护“惰性”
// 正在加载go.mod文件的“不变量”：
// None
// 1.模块的所选版本，提供标记为
// pkgInAll或pkgIsRoot都包含为根。
// 请注意，某些根模式（例如“…”）可能会分解根集
// 包含提供任何导入（或仅导入）包的每个模块
// 任何其他模块都需要。
// 2.每个根目录仅在其路径的选定版本显示一次
// （如果rs.graph为非nil）或最高版本，否则以
// 根（否则）。
// 3.在rs中显示为根的每个模块路径仍然是根。
// 4.add中的每个版本都在其给定版本中选择，除非由升级
// 添加中现有根或其他模块的依赖项。
// None
// 假定pkgs中的包是从
// rs或在rs图中选择的模块。
// None
// 上面的不变量一起暗示了应用程序的“延迟加载”不变量
// go.mod文件：
// None
// 1.（导入不变量。）以传递方式提供包的每个模块
// 由主模块中的任何包或测试导入的都包含为根。
// 接着是上文（1）和（3）的归纳。过渡进口
// 调用期间加载的包用pkgInAll（1）标记，
// 根据假设，在以前的版本中加载的任何可传递的导入包
// 调用已经是rs（3）中的根。
// None
// 2.（参数不变。）提供包匹配的每个模块
// 显式包模式包含为根。这是直接的
// from（1）：匹配显式包模式的包被标记为
// 根。
// None
// 3.（完整性不变量。）提供任何包的每个模块
// 主模块或其中一个模块需要对生成进行修改
// 它明确要求。这个不变量由调用者决定，调用者必须
// 不从模块图外部加载包，但可能会将根添加到
// 图，但由（3）简化。如果调用方将根添加到
// 为了解决丢失的包，updateLazyRoots将保留它们，
// 这些根的选定版本不能回归，而且它们将回归
// 最终会被写回主模块的go.mod文件。
// None
// （见https:
// 细节。）
func updateLazyRoots(ctx context.Context, direct map[string]bool, rs *Requirements, pkgs []*loadPkg, add []module.Version, rootsImported bool) (*Requirements, error) {
	roots := rs.rootModules
	rootsUpgraded := false

	spotCheckRoot := map[module.Version]bool{}

	// “模块的选定版本，提供标记为的每个包
	// pkgInAll或pkgIsRoot都包含在根目录中。“
	needSort := false
	for _, pkg := range pkgs {
		if !pkg.fromExternalModule() {
			// pkg不是从模块依赖项加载的，因此我们不需要
			// 做任何特殊的事情来维持这种依赖性。
			continue
		}

		switch {
		case pkg.flags.has(pkgInAll):
			// pkg是通过主模块中的包或测试传递导入的。
			// 我们需要将维护它的模块升级为根：如果有的话
			// 其他模块依赖于主模块，而其他模块也依赖于主模块
			// 使用延迟加载，它将期望找到所有可传递的
			// 只读取我们的go.mod文件，而不是
			// 我们所依赖的一切。
			// None
			// （这是使延迟加载成为可能的“导入不变量”。）

		case rootsImported && pkg.flags.has(pkgFromRoot):
			// pkg是某个根的可传递依赖项，我们正在处理
			// 根，就好像它们是由主模块导入的一样（如“go-get”）。

		case pkg.flags.has(pkgIsRoot):
			// pkg是包导入图的根。（通常这意味着
			// 它与命令行参数匹配。）我们希望以后调用
			// “go”命令-如同一软件包上的“go test”-继续
			// 使用与我们现在使用的相同版本的依赖项。
			// 因此，我们需要将这个包的依赖项放在延迟加载中
			// 地平线
			// None
			// 使包含此包的模块成为模块图的根
			// 确实是这样的：如果包含包的模块是惰性的，那么
			// 应满足导入不变量本身，因此其所有依赖项
			// 应该在其go.mod文件中，并且如果包含包的模块
			// 如果我们将它设为根，我们将加载它的所有可传递函数
			// 将依赖项导入到模块图中。
			// None
			// （这是延迟加载的“参数不变量”，对于
			// 再现性。）

		default:
			// pkg是主模块之外的其他包的依赖项。
			// 据我们所知，它与主模块无关（因此也不相关）
			// 与主模块的使用者相关），及其依赖关系
			// 应已在模块图中-包含在的依赖项中
			// 导入它的包。
			continue
		}

		if _, ok := rs.rootSelected(pkg.mod.Path); ok {
			// 主模块的go.mod文件可能不完整或不完整
			// 否则是错误的——例如，作者可能忘记了“git”
			// 添加“在添加新的包导入后添加其更新的go.mod文件，或
			// 也许他们使用第三方工具对go.mod文件进行了编辑
			// （'git merge'？）不维护模块的一致性
			// 依赖关系。如果发生这种情况，理想情况下，我们希望检测到丢失的信息
			// 需求，并在这里解决它们。
			// None
			// 然而，我们也需要小心，不要太咄咄逼人。对于
			// 外部测试的可传递依赖项
			// 包含测试本身的模块应提供所有
			// 相关的依赖项，并且我们明确地不想加入
			// 关于*不相关*需求的需求，这些需求碰巧发生在
			// 这些可传递的仅测试依赖项的go.mod文件。（参见测试
			// 在mod_lazy_test_horizon.txt中查看具体示例。
			// None
			// “金凤花区”似乎也在进行完全相同的抽查
			// 我们提升为显式根的模块：即提供
			// 由主模块传递导入的包，以及
			// 提供包导入图的根。那将是错误的
			// 对主模块go.mod文件的编辑以及中不一致的要求
			// 提供导入包但将忽略错误包的依赖项
			// 或者不明显的依赖项中的误导性需求
			// 与主模块中的包相关。
			spotCheckRoot[pkg.mod] = true
		} else {
			roots = append(roots, pkg.mod)
			rootsUpgraded = true
			// 根切片最初被排序，因为rs.rootModules被排序，
			// 但是我们刚才添加的根可能会出问题。
			needSort = true
		}
	}

	for _, m := range add {
		if v, ok := rs.rootSelected(m.Path); !ok || cmpVersion(v, m.Version) < 0 {
			roots = append(roots, m)
			rootsUpgraded = true
			needSort = true
		}
	}
	if needSort {
		module.Sort(roots)
	}

	// 每个根目录仅在其路径的选定版本上显示一次
	for {
		var mg *ModuleGraph
		if rootsUpgraded {
			// 我们已经添加或升级了一个或多个根，因此请加载整个模块
			// 这样我们就可以更新这些根，使之与其他根保持一致
			// 要求。
			if cfg.BuildMod != "mod" {
				// 我们对根的更改可能已将依赖项移入或移出
				// 延迟加载范围，这反过来可能会更改所选的
				// 其他模块的版本。（与急切模块不同，懒惰模块
				// 模块添加或删除显式根是一种语义更改，而不是
				// 只是一个化妆品。）
				return rs, errGoModDirty
			}

			rs = newRequirements(lazy, roots, direct)
			var err error
			mg, err = rs.Graph(ctx)
			if err != nil {
				return rs, err
			}
		} else {
			// 因为没有一个根被升级，我们没有理由这么做
			// 怀疑它们与任何其他文件的要求不一致
			// 根。如果我们已经加载了完整的模块图，请只查看它；
			// 否则，只需从中抽查根的明确要求即可
			// 我们装了包裹。
			if rs.graph.Load() != nil {
				// 我们已经加载了完整的模块图，其中包括
				// 所有根模块的需求-即使是可传递的
				// 要求，如果他们渴望的话！
				mg, _ = rs.Graph(ctx)
			} else if cfg.BuildMod == "vendor" {
				// 我们无法抽查其他模块的需求，因为我们
				// 一般来说，供应商没有提供他们的go.mod文件
				// 目录（幸运的是，这种情况是不可能的，因为mg.graph是
				// 在供应商模式下始终为非零！）
				panic("internal error: rs.graph is unexpectedly nil with -mod=vendor")
			} else if !spotCheckRoots(ctx, rs, spotCheckRoot) {
				// 我们抽查了根的明确要求，这些根是
				// 与我们加载的包相关。不幸的是，他们
				// 在某种程度上不一致；我们需要加载完整的模块图
				// 这样我们就可以把根固定好。
				var err error
				mg, err = rs.Graph(ctx)
				if err != nil {
					return rs, err
				}
			}
		}

		roots = make([]module.Version, 0, len(rs.rootModules))
		rootsUpgraded = false
		inRootPaths := make(map[string]bool, len(rs.rootModules)+1)
		inRootPaths[Target.Path] = true
		for _, m := range rs.rootModules {
			if inRootPaths[m.Path] {
				// 此根指定一个冗余路径。我们已经保留了
				// 当我们以前看到此路径时，选择了它的版本，因此省略
				// 冗余副本，无论其版本如何。
				// None
				// 当我们阅读完整的模块图时，我们包括
				// 每个根，即使该根是多余的。这样可以更好地保存
				// 再现性，例如，如果某个自动化工具添加了冗余
				// “require”行，然后运行“go mod tidy”，尝试使所有内容都保持整洁
				// 一致，因为旧版本的要求得到了满足
				// 结束
				// None
				// 因此，省略以前存在的根可能会减少
				// 非根目录的选定版本，但仅删除需求
				// 因此，无法*增加*其他根目录的选定版本-
				// 我们不需要将此更改标记为升级。（本节
				// 更改不能使任何其他根失效。）
				continue
			}

			var v string
			if mg == nil {
				v, _ = rs.rootSelected(m.Path)
			} else {
				v = mg.Selected(m.Path)
			}
			roots = append(roots, module.Version{Path: m.Path, Version: v})
			inRootPaths[m.Path] = true
			if v != m.Version {
				rootsUpgraded = true
			}
		}
		// 请注意，rs.rootModules已按模块路径和版本排序，
		// 我们以相同的顺序附加到根切片，并保证
		// 每个路径只有一个版本，因此根也按模块路径排序
		// 和（琐碎的）版本。

		if !rootsUpgraded {
			if cfg.BuildMod != "mod" {
				// 对根集（如果有）的唯一更改是删除重复项。
				// 需求是一致的（如果可能是多余的），因此保持
				// 原始rs保留其模块图。
				return rs, nil
			}
			// 根集合已经收敛：进入该迭代的每个根都是
			// 已在其选定版本中，尽管我们已删除其他
			// （冗余）相同路径的根。
			break
		}
	}

	if rs.depth == lazy && reflect.DeepEqual(roots, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
		// 根集没有改变，rs已经是惰性的，所以将rs保留为
		// 保留其缓存的ModuleGraph（如果有）。
		return rs, nil
	}
	return newRequirements(lazy, roots, direct), nil
}

// spotCheckRoots报告rs中根的版本是否满足
// mods中模块的明确要求。
func spotCheckRoots(ctx context.Context, rs *Requirements, mods map[module.Version]bool) bool {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	work := par.NewQueue(runtime.GOMAXPROCS(0))
	for m := range mods {
		m := m
		work.Add(func() {
			if ctx.Err() != nil {
				return
			}

			summary, err := goModSummary(m)
			if err != nil {
				cancel()
				return
			}

			for _, r := range summary.require {
				if v, ok := rs.rootSelected(r.Path); ok && cmpVersion(v, r.Version) < 0 {
					cancel()
					return
				}
			}
		})
	}
	<-work.Idle()

	if ctx.Err() != nil {
		// 要么我们抽查失败，要么打电话的人不再关心我们的工作
		// 无论如何回答。
		return false
	}

	return true
}

// TidyAgerRoots返回维护
// 在pkgs中提供软件包的每个模块的选定版本，以及
// 在direct中包含每个此类模块的选定版本作为根目录。
func tidyEagerRoots(ctx context.Context, direct map[string]bool, pkgs []*loadPkg) (*Requirements, error) {
	var (
		keep     []module.Version
		keptPath = map[string]bool{}
	)
	var (
		rootPaths   []string // 应包含为根的模块路径
		inRootPaths = map[string]bool{}
	)
	for _, pkg := range pkgs {
		if !pkg.fromExternalModule() {
			continue
		}
		if m := pkg.mod; !keptPath[m.Path] {
			keep = append(keep, m)
			keptPath[m.Path] = true
			if direct[m.Path] && !inRootPaths[m.Path] {
				rootPaths = append(rootPaths, m.Path)
				inRootPaths[m.Path] = true
			}
		}
	}

	min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep})
	if err != nil {
		return nil, err
	}
	return newRequirements(eager, min, direct), nil
}

// UpdateAgerRoots返回一组根需求，其中包括选定的
// 将每个模块路径的版本直接作为根目录，并维护选定的
// 在rs图中选择的每个模块的版本。
// None
// 根目录已更新，以便：
// None
// 1.direct中每个模块路径的选定版本都包含为根目录
// （如果不是“无”）。
// 2.每个根目录都是其路径的选定版本。（我们说这样的根
// 设置为“一致”。）
// 3.除非升级，否则rs图中选择的每个版本都将保持选中状态
// 通过add中的依赖项。
// 4.add中的每个版本都在其给定版本中选择，除非由升级
// 添加中现有根或其他模块的依赖项。
func updateEagerRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) {
	mg, err := rs.Graph(ctx)
	if err != nil {
		// 即使用户通过了-e，我们也不能忽略模块图中的错误
		// 试图推开他们的旗子。如果我们不能加载完整的模块
		// 依赖关系，那么我们就不能可靠地计算它们的最小子集。
		return rs, err
	}

	if cfg.BuildMod != "mod" {
		// 与其实际更新需求，不如检查是否没有更新
		// 这是需要的。
		if rs == nil {
			// 我们被要求从头开始重建需求，
			// 但我们甚至不允许修改它们。
			return rs, errGoModDirty
		}
		for _, m := range rs.rootModules {
			if m.Version != mg.Selected(m.Path) {
				// 根版本v有误导性：实际选择的版本更高。
				return rs, errGoModDirty
			}
		}
		for _, m := range add {
			if m.Version != mg.Selected(m.Path) {
				return rs, errGoModDirty
			}
		}
		for mPath := range direct {
			if _, ok := rs.rootSelected(mPath); !ok {
				// 模块m应该显式列出，但不是。
				// None
				// 请注意，也会检测到这种情况（并记录更多信息）
				// 细节）在包加载过程中提前，因此实际上不应该
				// 在这一点上是可能的-这只是一个纵深防御。
				return rs, errGoModDirty
			}
		}

		// 没有显式根丢失，并且所有根都已在版本中
		// 我们想保持稳定。我们所做的任何其他改变都只是表面上的，
		// 例如修剪冗余的间接依赖项。每期34822，我们
		// 当无法更新go.mod文件时，忽略外观更改。
		return rs, nil
	}

	var (
		rootPaths   []string // 应包含为根的模块路径
		inRootPaths = map[string]bool{}
	)
	for _, root := range rs.rootModules {
		// 如果选定的根目录版本与以前的版本相同
		// 在go.mod文件中列出，将其保留为根目录（即使是冗余的），以
		// 避免不必要的搅动。（见https:
		// None
		// 我们甚至对间接需求也这样做，因为我们不知道为什么会这样
		// 被添加，它们可以在任何时候变成直接的。
		if !inRootPaths[root.Path] && mg.Selected(root.Path) == root.Version {
			rootPaths = append(rootPaths, root.Path)
			inRootPaths[root.Path] = true
		}
	}

	// “direct中每个模块路径的选定版本都包含为根目录。”
	// None
	// 这只是为了方便最终用户和清晰起见：在急切的模块中，
	// 显式依赖与隐式依赖的选择对MVS没有影响
	// 选择（用于自身或任何其他模块）。
	keep := append(mg.BuildList()[1:], add...)
	for _, m := range keep {
		if direct[m.Path] && !inRootPaths[m.Path] {
			rootPaths = append(rootPaths, m.Path)
			inRootPaths[m.Path] = true
		}
	}

	min, err := mvs.Req(Target, rootPaths, &mvsReqs{roots: keep})
	if err != nil {
		return rs, err
	}
	if rs.depth == eager && reflect.DeepEqual(min, rs.rootModules) && reflect.DeepEqual(direct, rs.direct) {
		// 根集没有变化，rs已经很渴望了，所以让rs保持不变
		// 保留其缓存的ModuleGraph（如果有）。
		return rs, nil
	}
	return newRequirements(eager, min, direct), nil
}

// convertDepth返回具有给定深度的rs版本。
// 如果rs已经具有给定深度，则convertDepth返回rs未修改。
func convertDepth(ctx context.Context, rs *Requirements, depth modDepth) (*Requirements, error) {
	if rs.depth == depth {
		return rs, nil
	}

	if depth == eager {
		// 我们正在将一个懒惰的模块转换为一个渴望的模块。渴望的根源
		// 模图是惰性图根的超集，所以我们不需要
		// 需要添加任何新的根-我们只需要修剪掉那些是
		// 对于给定的渴望加载，这正是UpdateAgerRoots需要的
		// 做
		return updateEagerRoots(ctx, rs.direct, rs, nil)
	}

	// 我们正在将一个急切的模块转换为一个懒惰的模块。图的模图
	// 渴望模块包括模块中每个模块的可传递依赖项
	// 构建列表。
	// None
	// 嘿，我们可以将其表示为惰性根集！“包括及物动词
	// “构建列表中每个模块的依赖关系”正是在
	// 如果我们将构建列表中的每个模块升级到根目录，那么这就是一个懒惰的模块！
	mg, err := rs.Graph(ctx)
	if err != nil {
		return rs, err
	}
	return newRequirements(lazy, mg.BuildList()[1:], rs.direct), nil
}
